package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author atguigu-mqx
 * @Description TODO
 * @Date 2023/7/22 15:42
 * @Version 1.0
 */
@Service
@RefreshScope
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {


    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    /*
    ware:
    url: http://localhost:9001 地址改了，不需要重启代码，应该如何操作?
     */
    @Value("${ware.url}")
    private String wareUrl;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  order_info;
        //  total_amount order_status user_id out_trade_no trade_body operate_time expire_time process_status
        //  订单总金额=单价*数量; 调用这个方法之前，必须赋值订单明细集合。 orderInfo.orderDetailList 有数据的，从前端页面传递的！
        //  这个方法调用之后，自定给orderInfo 中的totalAmount属性赋值。
        orderInfo.sumTotalAmount();
        //  开始下订单的时候，订单状态应该是未支付.
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //  第三方支付时，需要使用的交易编号，这个交易编号不能重复，必须要唯一.
        String outTradeNo = "ATGUIGU"+System.currentTimeMillis()+new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //  订单主体描述.
        orderInfo.setTradeBody("硅谷商店");
        //  当前操作时间
        orderInfo.setOperateTime(new Date());
        //  赋值当前订单过期时间 +1天 30min 可以根据库存剩余数量来判断.
        //  Calendar 这个日期计算是线程不安全的。
        //        Calendar calendar = Calendar.getInstance();
        //        calendar.add(Calendar.DATE,1);
        //        orderInfo.setExpireTime(calendar.getTime());
        //  JDK1.8新出的操作时间安全类  LocalDate  LocalTime  LocalDateTime
        Instant instant = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault()).toInstant();
        //  将instant 对象变为 Date 对象
        orderInfo.setExpireTime(Date.from(instant));
        //  订单进度状态：
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        orderInfoMapper.insert(orderInfo);

        //  order_detail;
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            orderDetailList.forEach(orderDetail -> {
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            });
        }

        //  获取到订单Id
        Long orderId = orderInfo.getId();

        //  发送延迟消息：
        this.rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
        //  返回订单Id
        return orderId;
    }

    @Override
    public String getTradeNo(String userId) {
        //  声明一个变量
        String tradeNo = UUID.randomUUID().toString();
        //  定义缓存的key;
        String tradeNoKey = userId+":tradeNo";
        this.redisTemplate.opsForValue().set(tradeNoKey,tradeNo);
        //  页面存储的时候，
        //  返回流水号
        return tradeNo;
    }

    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  获取缓存的流水号
        //  定义缓存的key;
        String tradeNoKey = userId+":tradeNo";
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果.
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  删除缓存的流水号
        this.redisTemplate.delete(userId+":tradeNo");
    }

    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        //  http://localhost:9001/hasStock?skuId=xxx&num=xxx?  硬编码不好维护项目.
        //  把可能会发生变化的地址，通常写在配置文件中。这种方式：软编码.
        //  wareUrl=http://localhost:9001 要远程地址，应该使用 httpClient
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  返回值
        return "1".equals(result);
    }

    @Override
    public IPage<OrderInfo> getOrder(Page<OrderInfo> orderInfoPage, String userId, String orderStatus) {
        //  调用mapper 方法.
        IPage<OrderInfo> iPage = this.orderInfoMapper.selectOrder(orderInfoPage,userId,orderStatus);
        //  获取订单集合数据
        iPage.getRecords().forEach(orderInfo -> {
            orderInfo.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus()));
        });
        return iPage;
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  本质就是更新订单状态CLOSED.
        //  update order_info set order_stats = ?,process_status = ? where id = ?;
        //  根据订单Id 修改订单状态 PAID
        //  update order_info set order_stats = ?,process_status = ? where id = ?;
        //  订单状态可以从订单的进度状态中获取.
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        orderInfoMapper.updateById(orderInfo);
        //  关闭订单：
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //  支付成功：
        //  this.updateOrderStatus(orderId,ProcessStatus.PAID);
        //  发送消息：
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);

    }

    /**
     * 修改订单状态。
     * @param orderId
     * @param processStatus
     */
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        //  update order_info set order_stats = ?,process_status = ? where id = ?;
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());
        orderInfoMapper.updateById(orderInfo);

        //  有可能会关闭paymentInfo;

    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  获取订单信息
        //  OrderInfo orderInfo = this.getById(orderId);
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //  判断
        if (null!=orderInfo){
            //  查询订单明细.
            orderInfo.setOrderDetailList(orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId,orderId)));
        }
        //  返回订单对象.
        return orderInfo;
    }

    @Override
    public void sendMsgToWare(Long orderId) {
        //  修改订单状态.
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  先获取到订单明细.
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        // 根据发送的消息将orderInfo中的部分数据存储到一个map中。
        Map<String, Object> map = this.initWareJson(orderInfo);
        //  发送消息.
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));
    }

    /**
     * 将orderInfo 中的部分数据转换为Map 集合。
     * @param orderInfo
     * @return
     */
    public Map<String, Object> initWareJson(OrderInfo orderInfo) {
        //  创建一个Map集合
        Map<String, Object> map = new HashMap<>();
        map.put("orderId",orderInfo.getId());
        map.put("consignee", orderInfo.getConsignee());
        map.put("consigneeTel", orderInfo.getConsigneeTel());
        map.put("orderComment", orderInfo.getOrderComment());
        map.put("orderBody", orderInfo.getTradeBody());
        map.put("deliveryAddress", orderInfo.getDeliveryAddress());
        map.put("paymentWay", "2");
        map.put("wareId", orderInfo.getWareId());

        //  获取的是订单明细.
        /*
        details:[{skuId:101,skuNum:1,skuName:’小米手64G’},{skuId:201,skuNum:1,skuName:’索尼耳机’}]
        1.  转换为实体类
        2.  转换为map
         */
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            List<HashMap<String, Object>> mapList = orderDetailList.stream().map(orderDetail -> {
                HashMap<String, Object> detailMap = new HashMap<>();
                detailMap.put("skuId", orderDetail.getSkuId());
                detailMap.put("skuNum", orderDetail.getSkuNum());
                detailMap.put("skuName", orderDetail.getSkuName());
                return detailMap;
            }).collect(Collectors.toList());
            //  details
            map.put("details", mapList);
        }
        //  返回map 集合
        return map;
    }

    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
        //  创建一个子订单集合
        List<OrderInfo> arrayList = new ArrayList<>();
        //  1.  先获取到原始订单.
        OrderInfo orderInfoOrigin = this.getOrderInfo(Long.parseLong(orderId));
        //  2.  wareSkuMap:仓库Id 与商品skuId 对照关系. [{"wareId":"1","skuIds":["21","22"]},{"wareId":"2","skuIds":["23"]}]
        //  根据这个Json 字符串就能够判断要怎么拆单了.
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        if (!CollectionUtils.isEmpty(mapList)){
            //  获取到仓库Id 与 商品skuIds
            for (Map map : mapList) {
                String wareId = (String) map.get("wareId");
                List<String> skuIdsList = (List<String>) map.get("skuIds");
                //  3.  创建子订单并赋值
                OrderInfo subOrderInfo = new OrderInfo();
                BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);
                //  防止主键冲突
                subOrderInfo.setId(null);
                //  设置父级Id
                subOrderInfo.setParentOrderId(Long.parseLong(orderId));
                //  设置仓库Id
                subOrderInfo.setWareId(wareId);
                //  单独计算订单总金额.
                //  o: "21","22","23"  a. "21","22"  b. "23"
                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList().stream().filter(orderDetail -> skuIdsList.contains(orderDetail.getSkuId().toString())).collect(Collectors.toList());
                subOrderInfo.setOrderDetailList(orderDetailList);
                //  调用计算总金额方法。
                subOrderInfo.sumTotalAmount();
                //  4.  将子订单添加到集合中
                arrayList.add(subOrderInfo);
                //  5.  将子订单保存到数据库
                this.saveOrderInfo(subOrderInfo);
            }
        }
        //  修改原始订单状态
        this.updateOrderStatus(Long.parseLong(orderId),ProcessStatus.SPLIT);
        return arrayList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  orderInfo 必须要关闭的！
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //  判断是否要关闭paymentInfo
        if ("2".equals(flag)){
            //  发送消息：
            this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }
    }
}
